Conversation
📝 WalkthroughWalkthroughAdds a new StatsApi to fetch and aggregate sending statistics (multiple endpoints and grouped responses), new stats models and filter params with automatic list query serialization, an example script, unit tests, and bumps project version to 2.5.0. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client Code
participant MailtrapClient as MailtrapClient
participant GeneralApi as GeneralApi
participant StatsApi as StatsApi
participant HttpClient as HttpClient
participant MtApi as Mailtrap API
Client->>MailtrapClient: initialize(api_token)
Client->>MailtrapClient: access general_api.stats
MailtrapClient->>GeneralApi: request stats property
GeneralApi->>StatsApi: create/return StatsApi
Client->>StatsApi: get(account_id, params)
activate StatsApi
StatsApi->>StatsApi: _base_path(account_id)
StatsApi->>HttpClient: GET /api/accounts/{id}/stats?params
deactivate StatsApi
HttpClient->>MtApi: perform HTTP GET
MtApi-->>HttpClient: SendingStats JSON
HttpClient-->>StatsApi: parsed JSON
StatsApi->>StatsApi: construct SendingStats(...)
StatsApi-->>Client: SendingStats
Client->>StatsApi: by_domains(account_id, params)
activate StatsApi
StatsApi->>HttpClient: GET /api/accounts/{id}/stats/domains?params
deactivate StatsApi
HttpClient->>MtApi: perform HTTP GET
MtApi-->>HttpClient: grouped JSON array
HttpClient-->>StatsApi: parsed JSON
loop per item
StatsApi->>StatsApi: map item -> SendingStatGroup(name, value, stats)
end
StatsApi-->>Client: list[SendingStatGroup]
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
tests/unit/api/general/test_stats.py (1)
165-169: Assert the serialized list values, not just the key names.These checks still pass if the request encodes each list under the right key but with the wrong shape. Since this PR adds list-param serialization, it would be better to assert the exact repeated values in the query string.
Proposed test tightening
+from urllib.parse import parse_qs +from urllib.parse import urlparse + from typing import Any import pytest import responses @@ - request_params = responses.calls[0].request.params - assert "sending_domain_ids[]" in request_params - assert "sending_streams[]" in request_params - assert "categories[]" in request_params - assert "email_service_providers[]" in request_params + request_params = parse_qs(urlparse(responses.calls[0].request.url).query) + assert request_params["sending_domain_ids[]"] == ["1", "2"] + assert request_params["sending_streams[]"] == ["transactional"] + assert request_params["categories[]"] == ["Transactional"] + assert request_params["email_service_providers[]"] == ["Gmail"]🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/unit/api/general/test_stats.py` around lines 165 - 169, Update the assertions in the test that inspect responses.calls[0].request.params so they assert the serialized list values (not just the presence of keys); for example, replace the four assert "key in request_params" checks with assertions that request_params["sending_domain_ids[]"], request_params["sending_streams[]"], request_params["categories[]"], and request_params["email_service_providers[]"] equal the expected lists (or expected repeated values) used in the test input — this ensures the serialized shape and values produced by the list-param serialization logic (as observed via request_params) are exactly what the call should send.mailtrap/api/resources/stats.py (3)
1-4: Consider consolidating imports from the same module.The three imports from
mailtrap.models.statscan be combined into a single statement for cleaner code.♻️ Suggested consolidation
from mailtrap.http import HttpClient -from mailtrap.models.stats import SendingStatGroup -from mailtrap.models.stats import SendingStats -from mailtrap.models.stats import StatsFilterParams +from mailtrap.models.stats import SendingStatGroup, SendingStats, StatsFilterParams🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@mailtrap/api/resources/stats.py` around lines 1 - 4, Consolidate the three separate imports from mailtrap.models.stats into a single import statement: replace the individual imports of SendingStatGroup, SendingStats, and StatsFilterParams with one grouped import from mailtrap.models.stats so the top of the file imports HttpClient from mailtrap.http and imports SendingStatGroup, SendingStats, StatsFilterParams together from mailtrap.models.stats.
6-11: Consider makingGROUP_KEYSa private constant.Since
GROUP_KEYSis an internal implementation detail used only by_grouped_stats, prefixing it with an underscore (_GROUP_KEYS) would signal this intent and prevent external reliance on the mapping.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@mailtrap/api/resources/stats.py` around lines 6 - 11, Rename the public constant GROUP_KEYS to a private constant named _GROUP_KEYS and update all references to it (notably inside the _grouped_stats function) so the implementation detail is not exposed; ensure any import/usage within the same module uses _GROUP_KEYS and run tests to confirm there are no external references that need updating.
50-65: Consider stricter typing for thegroupparameter.The
groupparameter only accepts values fromGROUP_KEYS. Using aLiteraltype would provide better type safety and IDE support.♻️ Suggested type improvement
+from typing import Literal + +GroupType = Literal["domains", "categories", "email_service_providers", "date"] + def _grouped_stats( - self, account_id: int, group: str, params: StatsFilterParams + self, account_id: int, group: GroupType, params: StatsFilterParams ) -> list[SendingStatGroup]:🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@mailtrap/api/resources/stats.py` around lines 50 - 65, The group parameter to _grouped_stats only accepts keys from GROUP_KEYS, so declare a stricter Literal alias (e.g. GroupKey = Literal['keyA', 'keyB', ...] where the string literals enumerate the keys in GROUP_KEYS), replace the method signature to def _grouped_stats(self, account_id: int, group: GroupKey, params: StatsFilterParams) -> list[SendingStatGroup], and update any callers/type hints accordingly; keep the runtime behavior unchanged (still use GROUP_KEYS[group] and response parsing into SendingStatGroup).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@examples/general/stats.py`:
- Line 7: ACCOUNT_ID is declared as a string but passed to helper functions
typed to accept int; change the declaration of ACCOUNT_ID from a string to an
integer literal (e.g., ACCOUNT_ID = 123456) and update any other sample
declarations/usages (the block around the later occurrences) so they pass an int
to the helper functions referenced in this file; ensure references to ACCOUNT_ID
in functions like the stats helpers now receive an int to keep types consistent.
---
Nitpick comments:
In `@mailtrap/api/resources/stats.py`:
- Around line 1-4: Consolidate the three separate imports from
mailtrap.models.stats into a single import statement: replace the individual
imports of SendingStatGroup, SendingStats, and StatsFilterParams with one
grouped import from mailtrap.models.stats so the top of the file imports
HttpClient from mailtrap.http and imports SendingStatGroup, SendingStats,
StatsFilterParams together from mailtrap.models.stats.
- Around line 6-11: Rename the public constant GROUP_KEYS to a private constant
named _GROUP_KEYS and update all references to it (notably inside the
_grouped_stats function) so the implementation detail is not exposed; ensure any
import/usage within the same module uses _GROUP_KEYS and run tests to confirm
there are no external references that need updating.
- Around line 50-65: The group parameter to _grouped_stats only accepts keys
from GROUP_KEYS, so declare a stricter Literal alias (e.g. GroupKey =
Literal['keyA', 'keyB', ...] where the string literals enumerate the keys in
GROUP_KEYS), replace the method signature to def _grouped_stats(self,
account_id: int, group: GroupKey, params: StatsFilterParams) ->
list[SendingStatGroup], and update any callers/type hints accordingly; keep the
runtime behavior unchanged (still use GROUP_KEYS[group] and response parsing
into SendingStatGroup).
In `@tests/unit/api/general/test_stats.py`:
- Around line 165-169: Update the assertions in the test that inspect
responses.calls[0].request.params so they assert the serialized list values (not
just the presence of keys); for example, replace the four assert "key in
request_params" checks with assertions that
request_params["sending_domain_ids[]"], request_params["sending_streams[]"],
request_params["categories[]"], and request_params["email_service_providers[]"]
equal the expected lists (or expected repeated values) used in the test input —
this ensures the serialized shape and values produced by the list-param
serialization logic (as observed via request_params) are exactly what the call
should send.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ae6b307f-d0d4-4d77-8389-241e8f13796b
📒 Files selected for processing (9)
CHANGELOG.mdexamples/general/stats.pymailtrap/__init__.pymailtrap/api/general.pymailtrap/api/resources/stats.pymailtrap/models/common.pymailtrap/models/stats.pypyproject.tomltests/unit/api/general/test_stats.py
| Documentation = "https://github.com/railsware/mailtrap-python" | ||
| Repository = "https://github.com/railsware/mailtrap-python.git" | ||
| "API documentation" = "https://api-docs.mailtrap.io/" | ||
| "API documentation" = "https://docs.mailtrap.io/developers" |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
examples/general/stats.py (2)
11-55: Avoid baking January 2026 into every sample call.This example will look stale quickly and often return empty data unless the reader happens to have stats in that exact window. A shared
START_DATE/END_DATEplaceholder pair would keep the sample easier to update.Possible cleanup
+START_DATE = "YYYY-MM-DD" +END_DATE = "YYYY-MM-DD" + def get_stats(account_id: int) -> SendingStats: - params = StatsFilterParams(start_date="2026-01-01", end_date="2026-01-31") + params = StatsFilterParams(start_date=START_DATE, end_date=END_DATE)Apply the same replacement to the other helpers in this file.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/general/stats.py` around lines 11 - 55, The examples hard-code the January 2026 date range in every helper (e.g., get_stats, get_stats_by_domains, get_stats_by_categories, get_stats_by_email_service_providers, get_stats_by_date, get_stats_with_filters, get_stats_by_domains_with_filters); replace those literal start_date/end_date strings with shared constants (e.g., START_DATE and END_DATE) defined once at the top of the module and use them in all StatsFilterParams instances so the sample is easy to update in one place and avoid stale/empty results.
1-8: Load the sample token from the environment instead of source.A literal placeholder keeps secret scanners noisy and nudges copy-paste users toward editing credentials into the example.
Possible cleanup
+import os + import mailtrap as mt from mailtrap.models.stats import SendingStatGroup, SendingStats, StatsFilterParams -API_TOKEN = "YOUR_API_TOKEN" +API_TOKEN = os.environ["MAILTRAP_API_TOKEN"] ACCOUNT_ID = 123456🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/general/stats.py` around lines 1 - 8, Replace the hard-coded API_TOKEN and ACCOUNT_ID in examples/general/stats.py with environment variables: import os, read API_TOKEN via os.getenv('MAILTRAP_API_TOKEN') and ACCOUNT_ID via os.getenv('MAILTRAP_ACCOUNT_ID') (convert to int as needed), and keep a clear non-secret fallback or guard that raises a helpful error if the env var is missing before constructing MailtrapClient; update references to API_TOKEN and ACCOUNT_ID accordingly (symbols: API_TOKEN, ACCOUNT_ID, MailtrapClient, stats_api).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@examples/general/stats.py`:
- Around line 11-55: The examples hard-code the January 2026 date range in every
helper (e.g., get_stats, get_stats_by_domains, get_stats_by_categories,
get_stats_by_email_service_providers, get_stats_by_date, get_stats_with_filters,
get_stats_by_domains_with_filters); replace those literal start_date/end_date
strings with shared constants (e.g., START_DATE and END_DATE) defined once at
the top of the module and use them in all StatsFilterParams instances so the
sample is easy to update in one place and avoid stale/empty results.
- Around line 1-8: Replace the hard-coded API_TOKEN and ACCOUNT_ID in
examples/general/stats.py with environment variables: import os, read API_TOKEN
via os.getenv('MAILTRAP_API_TOKEN') and ACCOUNT_ID via
os.getenv('MAILTRAP_ACCOUNT_ID') (convert to int as needed), and keep a clear
non-secret fallback or guard that raises a helpful error if the env var is
missing before constructing MailtrapClient; update references to API_TOKEN and
ACCOUNT_ID accordingly (symbols: API_TOKEN, ACCOUNT_ID, MailtrapClient,
stats_api).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 56f1334c-0854-40b5-a3f1-6b65a2ff5b92
📒 Files selected for processing (6)
CHANGELOG.mdexamples/general/stats.pymailtrap/api/resources/stats.pymailtrap/models/stats.pypyproject.tomltests/unit/api/general/test_stats.py
🚧 Files skipped from review as they are similar to previous changes (4)
- tests/unit/api/general/test_stats.py
- pyproject.toml
- mailtrap/models/stats.py
- CHANGELOG.md
Motivation
Changes
How to test
N/A